﻿#include "FITKGraphObjectMeshInstance.h"

// VTK
#include <vtkTransformFilter.h>
#include <vtkMatrix4x4.h>
#include <vtkTransform.h>
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>

// Data
#include "FITK_Component/FITKAbaqusData/FITKAbaqusPart.h"
#include "FITK_Interface/FITKInterfaceModel/FITKAbstractAssInstance.h"
#include "FITK_Interface/FITKInterfaceStructural/FITKAbaMeshPartVTK.h"
#include "FITK_Interface/FITKInterfaceModel/FITKUnstructuredMesh.h"
#include "FITK_Interface/FITKInterfaceMesh/FITKMeshFeatureVTK.h"

// Color assign
#include "FITK_Interface/FITKInterfacePhysics/FITKAbstractSection.h"
#include "FITK_Interface/FITKInterfacePhysics/FITKSectionAssign.h"
#include "FITK_Interface/FITKInterfacePhysics/FITKMaterialAssign.h"

// Graph and filter
#include "FITK_Interface/FITKVTKAlgorithm/FITKHighOrderCellFilter.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKShellFeatureEdges.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITKColorMap.h"
#include "FITKColorMapManager.h"
#include "FITKGraphCommons.h"
#include "FITKGraphObjectModelHighlighting.h"

namespace Exchange
{
    FITKGraphObjectMeshInstance::FITKGraphObjectMeshInstance(AbaqusData::FITKDataCase* caseData, Interface::FITKAbstractAssInstance* instance)
        : FITKGraphObjectMeshModelBase(caseData, instance)
    {
        // Return if the input is nullptr.
        if (!instance)
        {
            return;
        }

        // Initialize.
        init();

        // Generate actor.
        generateGraph(instance);

        // Save the model information.
        m_graphInfo.Type = InstanceGraph;
        m_graphInfo.InstanceId = instance->getDataObjectID();

        // Set the default model render mode.
        setRenderModel(ModelViewMode::ModelShaded);
    }

    FITKGraphObjectMeshInstance::~FITKGraphObjectMeshInstance()
    {
        // Delete the transform and matrix manually.
        deleteVtkObj(m_transform);

        // Delete the filters manually.
        deleteVtkObj(m_transformFilterFace);
        deleteVtkObj(m_transformFilterEdge);
    }

    void FITKGraphObjectMeshInstance::init()
    {
        // Create the transform.
        m_transform = vtkTransform::New();

        Interface::FITKAbstractAssInstance* instance = dynamic_cast<Interface::FITKAbstractAssInstance*>(_dataObj);

        // Get the transform matrix from instance.
        if (instance)
        {
            FITKGraphCommons::GetInstanceTransform(instance, m_transform);
        }

        this->Superclass::init();
    }

    void FITKGraphObjectMeshInstance::update(bool forceUpdate)
    {
        // Update the geometry data.
        this->Superclass::update(forceUpdate);

        // Update the transform.
        Interface::FITKAbstractAssInstance* instance = dynamic_cast<Interface::FITKAbstractAssInstance*>(_dataObj);
        if (instance)
        {
            FITKGraphCommons::GetInstanceTransform(instance, m_transform);
        }

        // Update pipelines.
        if (!m_fActorFace || !m_fActorAddin || !m_fActorFeature)
        {
            return;
        }

        m_fActorFace->update();
        m_fActorAddin->update();
        m_fActorFeature->update();
    }

    vtkTransform* FITKGraphObjectMeshInstance::getTransform()
    {
        return m_transform;
    }

    void FITKGraphObjectMeshInstance::colorByDefault(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorDefault && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the default model color.
        // Down cast data object.
        Interface::FITKAbstractAssInstance* instance = dynamic_cast<Interface::FITKAbstractAssInstance*>(_dataObj);
        if (!instance)
        {
            return;
        }

        // Get part model data with the instance data.
        Interface::FITKAbstractModel* partModel = instance->getModel();

        // Set color.
        colorByDefaultForPart(partModel);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorDefault;
    }

    void FITKGraphObjectMeshInstance::colorByMaterial(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorMaterial && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the material's color.
        // Down cast data object.
        Interface::FITKAbstractAssInstance* instance = dynamic_cast<Interface::FITKAbstractAssInstance*>(_dataObj);
        if (!instance)
        {
            return;
        }

        // Get part model data with the instance data.
        Interface::FITKAbstractModel* partModel = instance->getModel();
        
        // Set color.
        colorByMaterialForPart(partModel);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorMaterial;
    }

    void FITKGraphObjectMeshInstance::colorBySection(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorSection && !forceUpdate)
        {
            return;
        }

        // Set the graph actors with the section's color.
        // Down cast data object.
        Interface::FITKAbstractAssInstance* instance = dynamic_cast<Interface::FITKAbstractAssInstance*>(_dataObj);
        if (!instance)
        {
            return;
        }

        // Get part model data with the instance data.
        Interface::FITKAbstractModel* partModel = instance->getModel();

        // Set color.
        colorBySectionForPart(partModel);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorSection;
    }

    void FITKGraphObjectMeshInstance::colorByDefaultMesh(bool forceUpdate)
    {
        // If the color mode is the same as current mode, return.
        if (m_colorMode == ColorMode::ColorDefaultMesh && !forceUpdate)
        {
            return;
        }

        // Down cast data object.
        Interface::FITKAbstractAssInstance* instance = dynamic_cast<Interface::FITKAbstractAssInstance*>(_dataObj);
        if (!instance)
        {
            return;
        }

        // Get part model data with the instance data.
        Interface::FITKAbstractModel* partModel = instance->getModel();

        // Set color.
        colorByDefaultMeshForPart(partModel);

        // Set the color mode value.
        m_colorMode = ColorMode::ColorDefaultMesh;
    }

    void FITKGraphObjectMeshInstance::generateGraph(Interface::FITKAbstractAssInstance* instance)
    {
        // Get part data with the instance data.
        Interface::FITKAbstractModel* model = instance->getModel();
        AbaqusData::FITKAbaqusPart* part = AbaqusData::FITKAbaqusPart::SafeDownCastFITKAbaqusPart(model);
        if (!part)
        {
            return;
        }

        // Save the model information.
        m_graphInfo.PartId = part->getDataObjectID();

        // Get mesh data from the part data.
        m_meshData = part->getMesh();
        if (!m_meshData)
        {
            return;
        }

        // Initialize the color map with the part id.
        m_colorMap = FITKColorMapManager::getInstance()->getPartColorMap(part->getDataObjectID());

        // Get surface data for visualization.
        m_featureMesh = m_meshData->getMeshFeature();
        if (!m_featureMesh)
        {
            return;
        }

        // Update mesh data.
        m_featureMesh->update();

        // Get vtk surface data from the mesh data, and save the feature edge filter for simplified model.
        m_highOrderCellFilter = m_featureMesh->getHighOrderFilter();
        m_featureEdgeFilter = m_featureMesh->getFeatureEdgeFilter();
        if (!m_highOrderCellFilter)
        {
            return;
        }

        // Create the transform filter for face.
        m_transformFilterFace = vtkTransformFilter::New();
        m_transformFilterFace->SetInputConnection(m_highOrderCellFilter->GetOutputPort());
        m_transformFilterFace->SetTransform(m_transform);

        // Initialize the actors.
        initActors();

        // Generate FITKGraphActor and set the port of transform filter.
        m_fActorFace->setInputConnection(m_transformFilterFace->GetOutputPort());
        m_fActorPoint->setInputConnection(m_transformFilterFace->GetOutputPort());

        if (m_graphObjHighlight)
        {
            m_graphObjHighlight->setModelInputConnection(m_transformFilterFace->GetOutputPort());
        }

        if (m_fActorAddin)
        {
            m_fActorAddin->setInputConnection(m_transformFilterFace->GetOutputPort());
        }

        // Create the transform filter for feature.
        m_transformFilterEdge = vtkTransformFilter::New();
        m_transformFilterEdge->SetInputConnection(m_featureEdgeFilter->GetOutputPort());
        m_transformFilterEdge->SetTransform(m_transform);

        // Initialize feature actor.
        //@{
        // Create feature actor for shell.
        m_isShellModel = (m_meshData->getMeshDim() == Interface::FITKModelEnum::FITKMeshDim::FMDimD2);
        if (m_isShellModel)
        {          
            // Create the feature actor and initialize the properties.
            initFeatureActor();

            // Generate FITKGraphActor and set the port of transformed feature filter.
            m_fActorFeature->setInputConnection(m_transformFilterEdge->GetOutputPort());
        }
        //@}

        // Save the model filter.
        m_modelFilter = m_transformFilterFace;
        m_modelEdgeFilter = m_transformFilterEdge;
    }
}   // namespace Exchange